#pragma once
//这个头文件用于制定一些业务协议
#include<iostream>
#include<string>
#include <sys/socket.h>
#include<vector>

using namespace std;

enum
{
    DIV_ZERO = 1,
    MOD_ZERO,
    OP_ERR
};

#define SEP " "
#define SEP_LEN strlen(SEP) //不能用sizeof
#define LINE_SEP "\r\n"
#define LINE_SEP_LEN strlen(LINE_SEP)  //不能用sizeof

// 将序列化的数据+上一个报头，形成一个新的报文
// "x op y" --> "content_len\r\n""x op y\r\n"
// "exitcode result" --> "content_len\r\n""exitcode result\r\n"
bool enLength(string& send_string, const string& text)
{
    string str = to_string(text.size());
    str += LINE_SEP;
    str += text;
    str += LINE_SEP;
    send_string = str;

    return true;
}

// 将读取到的数据，拿掉报头，拿到一个准确的有效载荷————序列化的数据
// "content_len\r\n""x op y\r\n" --> "x op y"
// "content_len\r\n""exitcode result" --> "exitcode result"
bool deLength(const string& package, string* text)
{
    auto pos = package.find(LINE_SEP);
    if(pos == string::npos)
        return false;
    string context_len = package.substr(0, pos);
    int text_len = stoi(context_len);
    *text = package.substr(pos + LINE_SEP_LEN, text_len); 
    
    return true;
}


// 由于实现的是计算器，因此就制定协议，比如接收到的必须是 x op y。 比如2 % 4
class Request
{
public:
    Request(int x = 0, char op = 0, int y = 0)
    :_x(x), _op(op), _y(y)
    {} 

    // 不管是请求还是响应都是要被序列化和反序列化的。因此都需要有序列化接口
    // 这个接口可以自己写，也可以用现成的 
    bool serialize(string* out) // 序列化接口
    {
        // 序列化其实就是结构化的数据转化成报文(序列字节流)
        // 结构化数据 —————> x op y
        string xstring = to_string(_x);
        string ystring = to_string(_y);
        
        *out = ""; //先进行初始化
        *out += xstring;
        *out += SEP;
        *out += _op;
        *out += SEP;
        *out += ystring;

        return true;
    }

    bool deserialize(const string& in) // 反序列化接口
    {
        // x op y -> 结构化数据
        // 简单来说就是提取出x op y 
        // 这里自己写可以用很多方法，比如循环，也可以直接用容器接口
        int left = in.find(SEP);
        int right = in.rfind(SEP);
        if(left == right)
            return false; //一定是两个分隔符
        if(left == string::npos || right == string::npos)
            return false;
        if((right - left - SEP_LEN) != 1) //有且只有一个操作符
            return false;
        
        //走到这里就是找到了两个分隔符，现在就是将x op y提取出来
        // 这里用substr实现子串截取，要注意参数, 第一个参数是起始位置，第二个参数是截取长度
        string x = in.substr(0, left);
        string y = in.substr(right + SEP_LEN, in.size() - right - SEP_LEN);
        // char op = in.substr(left + SEP_LEN, right - left - SEP_LEN)[0];
        char op = in[left + SEP_LEN]; //等价上面这句代码
        

        // 然后将提出出来的数据进行处理
        if(x.empty()) return false;
        if(y.empty()) return false;
        _x = stoi(x);
        _y = stoi(y);
        _op = op;

        return true;
    }

public:
    int _x;
    char _op;
    int _y;
};


class Response
{
public:
    Response()
    :_exitcode(0), _result(0)
    {}

    Response(int exitcode, int result)
    :_exitcode(exitcode), _result(result)
    {}

    bool serialize(string& out)
    {
        // 结构化数据 --> "_exitcode _result"
        string str_exitcode = to_string(_exitcode);
        string str_result = to_string(_result);

        out = "";
        out += str_exitcode;
        out += SEP;
        out += str_result;

        return true;
    }

    bool deserialize(const string& in)
    {
        // "_exitcode _result" --> 结构化数据
        auto mid = in.find(SEP);
        if(mid == string::npos)
            return false;
        // 截取子串
        string ex_string = in.substr(0, mid);
        string res_string = in.substr(mid + SEP_LEN);

        if(ex_string.empty() || res_string.empty())
            return false;

        _exitcode = stoi(ex_string);
        _result = stoi(res_string);
        return true;
    }
public:
int _exitcode; //定好标准，0表示计算成功，!0表示计算失败，遇到错误。具体是多少，自己定制，比如1是÷0错误
int _result; // 这是计算成功后的结果

};

// text是输出型参数
bool recvPackage(int sockfd, string& inbuffer, string* text)
{
    char buffer[1024];
    while(1)
    {
        ssize_t n = recv(sockfd, buffer, sizeof(buffer)-1, 0); //阻塞式读取，0代表读取方式，默认0即可
        if(n > 0) //大于0就是读到了n个字节长度
        {
            buffer[n] = 0; 
            inbuffer += buffer;
            // 此时inbuffer中+上了本次读取的数据，这个时候就要判断里面是否含有一份完整的报文———— "content_len\r\n""x op y\r\n"
            int pos = inbuffer.find(LINE_SEP); 
            if(pos == string::npos) //如果没找到的话就说明压根就不可能有完整报文
                continue;
            // 一旦能读到LINE_SEP, 就一定能找到content_len
            string content_str = inbuffer.substr(0, pos); //找到content_len之后，就能知道正文的长度
            int text_len = stoi(content_str);
            int total_len = content_str.size() + 2*LINE_SEP_LEN + text_len;
            // 此时拿到了一份完整的报文所应该拥有的长度
            if(inbuffer.size() < total_len) //一旦小于，就说明肯定没有一份完整的报文
            {
                cout << "读取到的内容的格式并不是遵守了协议的规定，因此等待后序的内容，continue\n";
                continue;
            }

            cout << "处理前的inbuffer: " << inbuffer << endl;
            
            // 走到这里就说明，，inbuffer里面肯定有一份完整的报文
            *text = inbuffer.substr(0, total_len); //将完整报文给切下来 
            //【注意这里不考虑发送方发的是乱序的， 序列字节流，只考虑，没发完整，即顺序是对的】
            inbuffer.erase(0, total_len); //将已经读取的报文给清除掉
            cout << "处理后的inbuffer: \n" << inbuffer << endl;
            break;
        }
        else if(n == 0) 
            return false;
        else
            return false;
    }

    return true;
}

// 下面这个函数表示可以同时接受多个报文，即多个进程调用该函数，每个进程将自己读到的报文都放到out这个vector中
// 但是由于recvRequest是阻塞式读取，这里就不适合
// bool recvRequestAll(int sockfd, vector<string>& out)
// {
//     string line;
//     while(recvRequest(sockfd, &line)) //不断循环的去读取，读到一个完整报文，就放到
//     {
//         out.push_back(line);
//     }
// }